package com.gogant.spider.core;

import java.net.URLConnection;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

final class QueueWorkerPool<Req extends Request, Resp extends Response, Conn extends URLConnection> {
	private List<QueueWorker<Req, Resp, Conn>> workerList = new ArrayList<QueueWorker<Req, Resp, Conn>>();
	private Queue<QueueWorker<Req, Resp, Conn>> idleWorkerQueue = new LinkedList<QueueWorker<Req, Resp, Conn>>();
	private Spider<Req, Resp, Conn> spider;
	private ConnectorFactory<? extends Spider<Req, Resp, Conn>, Req, Resp, Conn> connectorFactory;
	private int poolSize;
	private int idleTimeout;
	private int workInterval;

	QueueWorkerPool(Spider<Req, Resp, Conn> spider, int poolSize, ConnectorFactory<? extends Spider<Req, Resp, Conn>, Req, Resp, Conn> factory) {
		if (spider == null) {
			throw new IllegalArgumentException("Spider can not be null");
		}
		if (poolSize <= 0 || poolSize > 1024) {
			throw new IllegalArgumentException("illegal pool size: 1 <= " + poolSize + " <= 1024");
		}
		if (factory == null) {
			throw new IllegalArgumentException("ConnectorFactory can not be null");
		}
		this.spider = spider;
		this.poolSize = poolSize;
		this.connectorFactory = factory;
	}

	public ConnectorFactory<? extends Spider<Req, Resp, Conn>, Req, Resp, Conn> getConnectorFactory() {
		return connectorFactory;
	}

	/**
	 * 设置时间间隔
	 *
	 * @param workInterval
	 */
	public void setWorkInterval(int workInterval) {
		this.workInterval = workInterval;
	}

	/**
	 * 获取时间间隔
	 *
	 * @return
	 */
	public int getWorkInterval() {
		return workInterval;
	}

	/**
	 * 获取空闲超时时间
	 *
	 * @return
	 */
	public int getIdleTimeout() {
		return idleTimeout;
	}

	/**
	 * 设置空闲超时时间
	 *
	 * @param idleTimeout
	 */
	public void setIdleTimeout(int idleTimeout) {
		this.idleTimeout = idleTimeout;
	}

	/**
	 * 设置线程池大小
	 *
	 * @return
	 */
	public int getPoolSize() {
		return poolSize;
	}

	/**
	 * 获取当前线程数量
	 *
	 * @return
	 */
	public int getThreadCount() {
		synchronized (idleWorkerQueue) {
			int count = 0;
			for (QueueWorker<Req, Resp, Conn> worker : workerList) {
				if (worker.isAlive()) {
					count++;
				}
			}
			return count;
		}
	}

	/**
	 * 获取当前正在工作的线程数量
	 *
	 * @return
	 */
	public int getWorkingCount() {
		synchronized (idleWorkerQueue) {
			int count = 0;
			for (QueueWorker<Req, Resp, Conn> worker : workerList) {
				if (worker.isBusy()) {
					count++;
				}
			}
			return count;
		}
	}

	/**
	 * 提交任务
	 *
	 * @param context
	 * @throws SpiderException
	 */
	public void submit(Context context) throws SpiderException {
		if (context == null) {
			throw new SpiderException("request data can not be null");
		}
		QueueWorker<Req, Resp, Conn> worker = null;
		try {
			worker = getWorker(0);
		} catch (Exception e) {
		}
		if (worker == null) {
			throw new SpiderException("get idle queue worker failed");
		}
		worker.doWork(context);
	}

	/**
	 * 提交任务
	 *
	 * @param context
	 * @param timeout
	 * @throws SpiderException
	 */
	public void execute(Context context, long timeout) throws SpiderException {
		if (context == null) {
			throw new SpiderException("request data can not be null");
		}
		QueueWorker<Req, Resp, Conn> worker = null;
		try {
			worker = getWorker(timeout);
		} catch (Exception e) {
		}
		if (worker == null) {
			throw new SpiderException("get idle queue worker failed");
		}
		worker.doWork(context);
		worker.waitDone(context, timeout);
	}



	/**
	 * 销毁线程池
	 */
	public void destroy() {
		synchronized (idleWorkerQueue) {
			idleWorkerQueue.clear();
			for (QueueWorker<Req, Resp, Conn> worker : workerList) {
				worker.destroy();
			}
			workerList.clear();
			idleWorkerQueue.notifyAll();
		}
	}

	private QueueWorker<Req, Resp, Conn> getWorker(long timeout) {
		synchronized (idleWorkerQueue) {
			if (idleWorkerQueue.size() == 0) {
				if (poolSize <= 0) {
					return null;
				} else if (workerList.size() < poolSize) {
					QueueWorker<Req, Resp, Conn> worker = new QueueWorker<Req, Resp, Conn>(spider, this);
					workerList.add(worker);
					return worker;
				} else {
					try {
						if (timeout > 0) {
							idleWorkerQueue.wait(timeout);
						} else {
							idleWorkerQueue.wait();
						}
					} catch (Exception e) {
					}
					if (idleWorkerQueue.size() == 0) {
						return null;
					} else {
						return idleWorkerQueue.poll();
					}
				}
			} else {
				return idleWorkerQueue.poll();
			}
		}
	}

	protected void recycle(QueueWorker<Req, Resp, Conn> worker) {
		synchronized (idleWorkerQueue) {
			idleWorkerQueue.add(worker);
			idleWorkerQueue.notify();
		}
	}
}
